﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

using LowLevelGraphics.ColorSpaces;
using LowLevelGraphics.Types.Interval;

namespace LowLevelGraphics.Filter
{
    public delegate int InvertDelegate(int _nValue);

    /// <summary>
    /// This inverts a bitmap.
    /// Inversion is done by:
    /// 
    /// r = 255 - r
    /// g = 255 - g
    /// b = 255 - b
    /// </summary>
    public class Invert : BaseImageFilter
    {
        /// <summary>
        /// No params needed
        /// </summary>
        public Invert()
            : this(PROCESSING_MODE.Color)
        {
        }

        /// <summary>
        /// Invert 
        /// </summary>
        /// <param name="_processingMode"></param>
        public Invert(PROCESSING_MODE _processingMode)
        {
            M_PROCESSING_MODE = _processingMode;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_invert"></param>
        internal Invert(Invert _invert)
        {
            this.M_PROCESSING_MODE = _invert.M_PROCESSING_MODE;
        }

        /// <summary>
        /// Inverts the specified Bitmap.
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;

            byte r = 0;
            byte g = 0;
            byte b = 0;
            byte a = 0;
            byte iv = 255;

            int nHeight = bitmap.Height;
            int nBitmapWidth = bitmap.Width;
            int y = 0;
            int x = 0;

            Color colorGray = Color.Transparent;

            HSB hsb = null;
            byte nInverseValue = 255;
            switch (M_PROCESSING_MODE)
            {
                //invert gray value, this is the same as:
                //first convert to gray, then choose invert filter
                case PROCESSING_MODE.GrayValue:
                case PROCESSING_MODE.GrayColor:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            colorGray = ColorFunctions.GrayColor(Color.FromArgb(r, g, b));
                            bitmap.SetPixelDirect(x, y, (byte)(nInverseValue - colorGray.R), (byte)(nInverseValue - colorGray.G), (byte)(nInverseValue - colorGray.B));
                        }
                    }
                    break;

                //inverts only red channel
                case PROCESSING_MODE.R:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            a = (byte)(nInverseValue - a);
                            bitmap.SetPixelDirect(x, y, r, b, g);
                        }
                    }
                    break;

                //inverts only green channel
                case PROCESSING_MODE.G:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            g = (byte)(nInverseValue - g);
                            bitmap.SetPixelDirect(x, y, r,b,g);
                        }
                    }
                    break;

                //inverts only blue channel
                case PROCESSING_MODE.B:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            b = (byte)(nInverseValue - b);
                            bitmap.SetPixelDirect(x, y, r, b, g);
                        }
                    }
                    break;

                //inverts only brightness
                case PROCESSING_MODE.Brightness:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            hsb = new HSB(Color.FromArgb(r, g, b));
                            hsb.B = 1.0f - hsb.B;
                            bitmap.SetPixelDirect(x, y, hsb.ToColor());
                        }
                    }
                    break;

                //inverts only saturation
                case PROCESSING_MODE.Saturation:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            hsb = new HSB(Color.FromArgb(r, g, b));
                            hsb.S = 1.0f - hsb.S;
                            bitmap.SetPixelDirect(x, y, hsb.ToColor());
                        }
                    }
                    break;

                //inverts only hue
                case PROCESSING_MODE.Hue:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            hsb = new HSB(Color.FromArgb(r, g, b));
                            hsb.H = 360.0f - hsb.H;
                            bitmap.SetPixelDirect(x, y, hsb.ToColor());
                        }
                    }
                    break;

                //should invert only luminance
                //case PROCESSING_MODE.Luminance:
                //    InvertBitmap(_bitmap);
                    break;

                case PROCESSING_MODE.Color:
                    // GDI+ still lies to us - the return format is BGR, NOT RGB.
                    BitmapData bmData = bitmap.BitmapData; //bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                    int stride = bmData.Stride;
                    IntPtr Scan0 = bmData.Scan0;

                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nBitmapWidth; x++)
                        {
                            bitmap.GetPixelDirect(x, y, out a, out r, out g, out b);
                            r ^= 0xFF;
                            b ^= 0xFF;
                            g ^= 0xFF;
                            bitmap.SetPixelDirect(x, y, a, r, g, b);
                        }
                    }
                    break;
            }

            return _bitmap; //bitmap.UnlockBits(bmData);
        }

        /// <summary>
        /// Inverts the bitmap
        /// </summary>
        /// <param name="bitmap"></param>
        private void InvertBitmap(UnsafeBitmap bitmap)
        {
            BitmapData bmData = bitmap.BitmapData; //bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int nHeight = bitmap.Height;
            int nBitmapWidth = bitmap.Width;
            int stride = bmData.Stride;
            IntPtr Scan0 = bmData.Scan0;
            unsafe
            {
                int y = 0;
                int x = 0;
                byte* p = (byte*)(void*)Scan0;

                int nOffset = bitmap.OffsetPointerIncrease; //stride - nBitmapWidth * 3;
                int nByteCount = bitmap.BytesPerPixelCount;
                int nWidth = nBitmapWidth * nByteCount;

                for (y = 0; y < nHeight; ++y)
                {
                    for (x = 0; x < nWidth; ++x)
                    {
                        p[0] = (byte)(255 - p[0]);
                        ++p;
                    }
                    p += nOffset;
                }
            }
        }

        /// <summary>
        /// Use this to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Invert(this);
        }
    }
}
